﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using Snowdreamist.Runtime.Services;
using Snowdreamist.Threading.Primitives;

namespace Snowdreamist.Threading
{
    public class UIDispatcher : IUIDispatcher
    {
        public bool IsCurrentUIThread
        {
            get
            {
                return Thread.CurrentThread.ManagedThreadId == Application.Current.Dispatcher.Thread.ManagedThreadId;
            }
        }

        public void InvokeAsync(Delegate method, params object[] parameters)
        {
            Application.Current.Dispatcher.BeginInvoke(method, parameters);
        }

        public IAsyncResult BeginInvoke(Delegate method, AsyncCallback callback, object state, params object[] parameters)
        {
            return this.InternalAsyncInvoke(method, callback, state, parameters);
        }

        public IAsyncResult BeginInvoke(Action method, AsyncCallback callback, object state)
        {
            return this.BeginInvoke(method, callback, state, null);
        }

        public IAsyncResult BeginInvoke<T>(Action<T> method, AsyncCallback callback, object state, T arg)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg });
        }

        public IAsyncResult BeginInvoke<T1, T2>(Action<T1, T2> method, AsyncCallback callback, object state, T1 arg1, T2 arg2)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3>(Action<T1, T2, T3> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4, arg5 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 });
        }

        public IAsyncResult BeginInvoke<TResult>(Func<TResult> method, AsyncCallback callback, object state)
        {
            return this.BeginInvoke(method, callback, state, null);
        }

        public IAsyncResult BeginInvoke<T, TResult>(Func<T, TResult> method, AsyncCallback callback, object state, T arg)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg });
        }

        public IAsyncResult BeginInvoke<T1, T2, TResult>(Func<T1, T2, TResult> method, AsyncCallback callback, object state, T1 arg1, T2 arg2)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4, arg5 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, TResult> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 });
        }

        public IAsyncResult BeginInvoke<T1, T2, T3, T4, T5, T6, T7, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, TResult> method, AsyncCallback callback, object state, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7)
        {
            return this.BeginInvoke(method, callback, state, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 });
        }

        public object EndInvoke(Delegate method, IAsyncResult context)
        {
            if (!(context is AsyncContext))
                throw new ArgumentException("IAsyncResult not match");
            AsyncContext asyncContext = (AsyncContext)context;
            if (asyncContext.Error != null)
                throw asyncContext.Error;
            else
                return asyncContext.Result;
        }

        public void EndInvoke(Action method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T>(Action<T> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T1, T2>(Action<T1, T2> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T1, T2, T3>(Action<T1, T2, T3> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public void EndInvoke<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> method, IAsyncResult context)
        {
            this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<TResult>(Func<TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T, TResult>(Func<T, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T1, T2, TResult>(Func<T1, T2, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public TResult EndInvoke<T1, T2, T3, T4, T5, T6, T7, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, TResult> method, IAsyncResult context)
        {
            return (TResult)this.EndInvoke((Delegate)method, context);
        }

        public virtual void Initialize() { }

        public virtual void OnRuntimeDisposing() { }

        #region private

        private IAsyncResult InternalAsyncInvoke(Delegate delg, AsyncCallback callback, object state, object[] param)
        {
            // build async context
            AsyncContext context = new AsyncContext(new ManualResetEvent(false));
            // init
            context.Callback = callback;
            context.AsyncState = state;
            context.IsCompleted = false;
            context.CompletedSynchronously = true;
            context.Action = delg;
            context.Parameters = param;
            // invoke
            Application.Current.Dispatcher.BeginInvoke(new Action<AsyncContext>(this.AsyncInvokeCore), context);
            // return
            return context;
        }

        private void AsyncInvokeCore(AsyncContext context)
        {
            try
            {
                context.Result = context.Action.DynamicInvoke(context.Parameters);
            }
            catch (Exception error)
            {
                context.Error = error;
            }
            // done
            context.IsCompleted = true;
            context.SetEvent();
            if (context.Callback != null)
                context.Callback(context);
        }

        #endregion private
    }
}